package fileutil

import (
	"archive/zip"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"regexp"

	"github.com/h2non/filetype"
	"github.com/h2non/filetype/types"
)

func FileType(filePath string) (types.Type, error) {
	file, _ := os.Open(filePath)

	head := make([]byte, 261)
	_, _ = file.Read(head)
	return filetype.Match(head)
}

func FileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}

	return false, err
}

func DirExists(path string) (bool, error) {
	fileInfo, err := os.Stat(path)
	if err == nil && fileInfo.IsDir() {
		return true, nil
	}

	return false, fmt.Errorf("<%s> either doesn't exist or is not a directory", err)
}

func Touch(path string) error {
	_, err := os.Stat(path)
	if os.IsNotExist(err) {
		file, err := os.Create(path)
		if err != nil {
			return err
		}
		defer file.Close()
	}
	return nil
}

// 创建指定目录
func EnsureDir(path string) error {
	exists, err := FileExists(path)
	if !exists {
		err = os.Mkdir(path, 0755)
		return err
	}
	return err
}

// 创建指定路径上的所有目录
func EnsureDirAll(path string) error {
	return os.MkdirAll(path, 0755)
}

// 删除整个文件夹或文件
func RemoveDir(path string) error {
	return os.RemoveAll(path)
}

// 清空文件夹中的内容，保留文件夹
func EmptyDir(path string) error {
	d, err := os.Open(path)
	if err != nil {
		return err
	}
	defer d.Close()

	names, err := d.Readdirnames(-1)
	if err != nil {
		return err
	}

	for _, name := range names {
		err := os.RemoveAll(filepath.Join(path, name))
		if err != nil {
			return err
		}
	}

	return nil
}

// 获取当前用户家目录
func GetHomeDirectory() string {
	currentUser, err := user.Current()
	if err != nil {
		panic(err)
	}

	return currentUser.HomeDir
}

// 把文件 src 移动到 dst
func SafeMove(src, dst string) error {
	err := os.Rename(src, dst)
	if err != nil {
		fmt.Printf("[fileutil] unable to rename: \"%s\" due to %s. Falling back to copying.", src, err.Error())

		in, err := os.Open(src)
		if err != nil {
			return err
		}
		defer in.Close()

		out, err := os.Open(dst)
		if err != nil {
			return err
		}
		defer out.Close()

		_, err = io.Copy(out, in)
		if err != nil {
			return err
		}

		err = os.Remove(src)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsZipFileUncompressed returns true if zip file in path is using 0 compression level.
func IsZipFileUncompressed(path string) (bool, error) {
	r, err := zip.OpenReader(path)
	if err != nil {
		fmt.Printf("Error reading zip file %s: %s\n", path, err)
		return false, err
	}
	defer r.Close()
	for _, f := range r.File {
		if f.FileInfo().IsDir() { // skip dirs, they always get store level compression
			continue
		}
		return f.Method == 0, nil // check compression level of first actual  file
	}
	return false, nil
}

func WriteFile(path string, content []byte) error {
	err := EnsureDirAll(filepath.Dir(path))
	if err != nil {
		return fmt.Errorf("cannot ensure path %s", err)
	}

	err = os.WriteFile(path, content, 0600)
	if err != nil {
		return fmt.Errorf("write error for thumbnail %s: %s ", path, err)
	}

	return nil
}

func GetParent(path string) *string {
	isRoot := path[len(path)-1:] == "/"
	if isRoot {
		return nil
	}

	parentPath := filepath.Clean(path + "/..")
	return &parentPath
}

func ServeFileNoCache(w http.ResponseWriter, r *http.Request, filepath string) {
	w.Header().Add("Cache-Control", "no-cache")

	http.ServeFile(w, r, filepath)
}

func MatchEntries(dir, pattern string) (res []string, err error) {
	re, err := regexp.Compile(pattern)
	if err != nil {
		return nil, err
	}

	f, err := os.Open(dir)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	files, err := f.Readdirnames(-1)
	if err != nil {
		return nil, err
	}

	for _, file := range files {
		if re.Match([]byte(file)) {
			res = append(res, filepath.Join(dir, file))
		}
	}

	return
}
